/*******************************************************************************
  * 文件：NetComm_Comm.c
  * 作者：zyz
  * 版本：v1.0.0
  * 日期：2017-08-03
  * 说明：网络通信
*******************************************************************************/
/* 头文件 *********************************************************************/
#include "NetComm_Comm.h"
#include "NetComm_DispatchMsg.h"
#include "NetComm_Message.h"
#include "NetComm_Device.h"
#include "NetComm_WiFiCfg.h"
#include "Hardware_Uart.h"
#include <string.h>

/* 宏定义 *********************************************************************/
/* 类型定义 *******************************************************************/
/* 变量定义 *******************************************************************/
// 网络通信
static NetComm_ts sNetComm;

/* 函数声明 *******************************************************************/
static Bool NetComm_GetAckCmd(U8 u8Cmd, U8 *pu8AckCmd);    		// 获取应答命令
static void NetComm_CheckSendRequest(void);                	// 检查发送请求
static void NetComm_SendPacketDelayTimerCallback(void);    	// 发送包延时定时器回调函数
static void NetComm_CheckStatusTimerCallback(void);        	// 检测状态定时器回调函数

/* 函数定义 *******************************************************************/

/*******************************************************************************
  * 函数名：NetComm_Init
  * 功  能：初始化
  * 参  数：无
  * 返回值：无
  * 说  明：无
*******************************************************************************/
void NetComm_Init(void)
{
    // 初始化端口
    sNetComm.pfSendByte   = Hardware_WiFiSendByte;
    sNetComm.pfRecvByte   = Hardware_ShellRecvByte;
    // sNetComm.pfClearError = Uart4_ClearErrFlag;
    // sNetComm.pfTxIrqCtrl  = Uart4_TxIrqCtrl;
	// 初始化状态
    sNetComm.sSendControl.eState = eNETCOMM_STATE_IDLE;
    sNetComm.sRecvControl.eState = eNETCOMM_STATE_IDLE;
    // wifi配网检测初始化
    NetComm_WiFICfgInit();
    // 初始化队列
    OS_QueueInit(&sNetComm.sSendQueue, sNetComm.asSendBuffer,
               U8_NETCOMM_SEND_QUEUE_SIZE, sizeof(NetCommPacket_ts));
    OS_QueueInit(&sNetComm.sRecvQueue, sNetComm.asRecvBuffer,
               U8_NETCOMM_RECV_QUEUE_SIZE, sizeof(NetCommPacket_ts));
               
	// 开启定时器，定时检测状态是否改变，变化上报
    OS_TimerStart(&sNetComm.sCheckStatusTimer,
                  U16_NETCOMM_CHECK_STATUS_MSEC,
                  NetComm_CheckStatusTimerCallback);    // 检测状态变化定时器，变化上报
}

/*******************************************************************************
  * 函数名：NetComm_SetSendPacket
  * 功  能：构建发送数据包
  * 参  数：U8 u8Cmd - 命令
  * 返回值：数据起始地址
  * 说  明：无
*******************************************************************************/
U8* NetComm_SetupSendPacket(U8 u8Cmd)
{
    // 设置命令
    sNetComm.sSendPacket.u8Cmd = u8Cmd;
    // 清零保留字节
    memset((U8*)sNetComm.sSendPacket.au8ReserveBytes,
           0,
           U8_NETCOMM_PKT_RSRV_BYTES_LEN);
    // 返回数据起始地址
    return (U8*)sNetComm.sSendPacket.au8Data;
}

/*******************************************************************************
  * 函数名：NetComm_SendPacket
  * 功  能：发送数据包
  * 参  数：U8* pu8Checksum - 校验和指针
  * 返回值：无
  * 说  明：无
*******************************************************************************/
void NetComm_SendPacket(U8* pu8Checksum)
{
    U8 u8Index;
    U16 u16Checksum = 0;
    U8* pu8Data = &sNetComm.sSendPacket.u8Length;

    // 计算帧长
    sNetComm.sSendPacket.u8Length = (U8)(pu8Checksum -
                                         (&sNetComm.sSendPacket.u8Length));
    // 计算校验和
    for(u8Index=0; u8Index < sNetComm.sSendPacket.u8Length; u8Index++)
    {
        // 数据累加
        u16Checksum += *pu8Data;
        // 帧头字节后的冗余字节计入校验和
        if(*pu8Data == U8_NETCOMM_PKT_HEADER_BYTE)
        {
            u16Checksum += U8_NETCOMM_PKT_REDUNDANT_BYTE;
        }
        pu8Data++;
    }
    *pu8Checksum = (U8)u16Checksum;
    // 添加到发送队列，检查发送请求
    OS_QueueAdd(&sNetComm.sSendQueue, &sNetComm.sSendPacket);
    NetComm_CheckSendRequest();
}

/*******************************************************************************
  * 函数名：NetComm_GetAckCmd
  * 功  能：获取应答命令
  * 参  数：U8 u8Cmd      - 请求命令
  *         U8 *pu8AckCmd - 应答命令
  * 返回值：TRUE,需要应答;FALSE,不需要应答
  * 说  明：无
*******************************************************************************/
static Bool NetComm_GetAckCmd(U8 u8Cmd, U8 *pu8AckCmd)
{
    Bool bResult = TRUE;

    // 报警状态，设备状态上报，
    // 获取密钥，清除配置信息
    if((u8Cmd == U8_NETCOMM_CMD_04_RPT_ALARM_STATUS) ||
       (u8Cmd == U8_NETCOMM_CMD_06_RPT_STATUS_INFO)  ||
       (u8Cmd == U8_NETCOMM_CMD_63_GET_ENCRYPT_PARA) ||
       (u8Cmd == U8_NETCOMM_CMD_F8_CLEAR_CFG_INFO)   ||
	   (u8Cmd == U8_NETCOMM_CMD_12_DEV_UPWARD))		
    {
        *pu8AckCmd = U8_NETCOMM_CMD_05_ACK;
    }
    // 查询网络状态
    else if(u8Cmd == U8_NETCOMM_CMD_F0_QUERY_NET_STATUS)
    {
        *pu8AckCmd = U8_NETCOMM_CMD_F1_REPLY_NET_STATUS;
    }
    // 进入配置模式
    else if(u8Cmd == U8_NETCOMM_CMD_F2_ENTER_CFG_MODE)
    {
        *pu8AckCmd = U8_NETCOMM_CMD_F3_REPLY_ENTER_CFG_MODE;
    }
    // 进入工作模式
    else if(u8Cmd == U8_NETCOMM_CMD_F4_ENTER_WORK_MODE)
    {
        *pu8AckCmd = U8_NETCOMM_CMD_F5_REPLY_ENTER_WORK_MODE;
    }
    // 请求切换波特率
    else if(u8Cmd == U8_NETCOMM_CMD_E7_REQ_SWITCH_BAUDRATE)
    {
        *pu8AckCmd = U8_NETCOMM_CMD_E8_RES_SWITCH_BAUDRATE;
    }
    // 请求获取固件
    else if(u8Cmd == U8_NETCOMM_CMD_E5_REQ_GET_FIRMWARE)
    {
        *pu8AckCmd = U8_NETCOMM_CMD_E6_RES_GET_FIRMWARE;
    }
    // 上报升级结果
    else if(u8Cmd == U8_NETCOMM_CMD_ED_RPT_UPGRADE_RESULT)
    {
        *pu8AckCmd = U8_NETCOMM_CMD_EF_REPLY_UPGRADE_RESULT;
    }
	// 设备查询帧
	else if(u8Cmd == U8_NETCOMM_CMD_15_DEV_QUERY)
	{
		*pu8AckCmd = U8_NETCOMM_CMD_16_DEV_QUERY_ACK;
	}
    // 不需要应答命令
    else
    {
        bResult = FALSE;
    }

    // 返回结果
    return bResult;
}

/*******************************************************************************
  * 函数名：NetComm_CheckSendRequest
  * 功  能：检查发送请求
  * 参  数：无
  * 返回值：无
  * 说  明：无
*******************************************************************************/
static void NetComm_CheckSendRequest(void)
{
    // 发送状态空闲且发送队列有待发送数据包，开始发送数据
    if((sNetComm.sSendControl.eState == eNETCOMM_STATE_IDLE) &&
       (OS_QueueRemove(&(sNetComm.sSendQueue), &(sNetComm.sSendControl.sPacket))))
    {
		// 清零发送次数
        sNetComm.sSendControl.u8SentTimes = 0;
        // 清除等待应答标志
        sNetComm.sSendControl.bWaitAck = FALSE;
        // 设置“发送帧头”状态
        sNetComm.sSendControl.eState = eNETCOMM_STATE_HEADER;
        // 发送帧头字节 (首字节)
        sNetComm.pfSendByte(U8_NETCOMM_PKT_HEADER_BYTE);
    }
}

/*******************************************************************************
  * 函数名：NetComm_SendByteHandler
  * 功  能：发送字节处理
  * 参  数：无
  * 返回值：无
  * 说  明：在发送中断中运行
*******************************************************************************/
void NetComm_SendByteHandler(void)
{
    switch(sNetComm.sSendControl.eState)
    {
        // “发送帧头”状态
        case eNETCOMM_STATE_HEADER:
        {
            // 设置“发送帧长”状态
            sNetComm.sSendControl.eState = eNETCOMM_STATE_LENGTH;
            // 发送第二个帧头字节
            sNetComm.pfSendByte(U8_NETCOMM_PKT_HEADER_BYTE);
        } break;
        // “发送帧长”状态
        case eNETCOMM_STATE_LENGTH:
        {
            // 设置“发送帧体”状态
            sNetComm.sSendControl.eState = eNETCOMM_STATE_BODY;
            // 初始化发送指针和字节数
            sNetComm.sSendControl.pu8Byte =
                (U8*)(sNetComm.sSendControl.sPacket.au8ReserveBytes);
            sNetComm.sSendControl.u8ByteCount = 0;
            // 发送帧长字节
            sNetComm.pfSendByte(sNetComm.sSendControl.sPacket.u8Length);
        } break;
        // “发送帧体”状态
        case eNETCOMM_STATE_BODY:
        {
            // 发送完成
            if(sNetComm.sSendControl.u8ByteCount >=
               sNetComm.sSendControl.sPacket.u8Length)
            {
				// 设置“发送结束”状态
                sNetComm.sSendControl.eState = eNETCOMM_STATE_END;
                // 更新发送次数
                sNetComm.sSendControl.u8SentTimes++;
                // 获取应答命令
                if(sNetComm.sSendControl.u8SentTimes == 1)
                {
                    sNetComm.sSendControl.bWaitAck =
                    NetComm_GetAckCmd(sNetComm.sSendControl.sPacket.u8Cmd,
                                          &(sNetComm.sSendControl.u8AckCmd));
                }
				
                // 开启发送定时器
                OS_TimerRestart(&sNetComm.sSendTimer,
                              U16_NETCOMM_SEND_DELAY_MSEC,
                              NetComm_SendPacketDelayTimerCallback);
            }
            // 未发送完成
            else
            {
                // 上一个发送字节是帧头字节
                if((sNetComm.sSendControl.u8ByteCount > 0) &&
                   (sNetComm.sSendControl.u8LastByte == U8_NETCOMM_PKT_HEADER_BYTE))
                {
                    // 更新上一个发送字节
                    sNetComm.sSendControl.u8LastByte = U8_NETCOMM_PKT_REDUNDANT_BYTE;
                    // 更新发送字节数
                    sNetComm.sSendControl.u8ByteCount++;
                    // 发送冗余字节
                    sNetComm.pfSendByte(U8_NETCOMM_PKT_REDUNDANT_BYTE);
                }
                // 上一个发送字节不是帧头字节
                else
                {
                    // 更新上一个发送字节
                    sNetComm.sSendControl.u8LastByte = *(sNetComm.sSendControl.pu8Byte);
                    // 当前字节不是帧头字节，更新发送字节数
                    if(*(sNetComm.sSendControl.pu8Byte) != U8_NETCOMM_PKT_HEADER_BYTE)
                    {
                        sNetComm.sSendControl.u8ByteCount++;
                    }
                    // 发送当前字节
                    sNetComm.pfSendByte(*(sNetComm.sSendControl.pu8Byte++));
                }
            }
        } break;
        // 其他状态
        default:
        {
            // 重置为空闲状态
            sNetComm.sSendControl.eState = eNETCOMM_STATE_IDLE;
        }
        break;
    }
}

/*******************************************************************************
  * 函数名：NetComm_SendPacketDelayTimerCallback
  * 功  能：发送包延时定时器回调函数
  * 参  数：无
  * 返回值：无
  * 说  明：无
*******************************************************************************/
static void NetComm_SendPacketDelayTimerCallback(void)
{
	// 不需要应答或者达到最大重发次数
    if((sNetComm.sSendControl.bWaitAck == FALSE) ||
       (sNetComm.sSendControl.u8SentTimes > u8NETCOMM_RETRY_SEND_TIMES))
    {
        // 停止定时器
        OS_TimerStop(&sNetComm.sSendTimer);
        // 清除等待应答标志
        sNetComm.sSendControl.bWaitAck = FALSE;
        // 清零发送次数
        sNetComm.sSendControl.u8SentTimes = 0;
        // 设置“发送空闲”状态
        sNetComm.sSendControl.eState = eNETCOMM_STATE_IDLE;
        // 检查发送请求
        NetComm_CheckSendRequest();
    }
	// 等待应答，超时后重发
    else if(OS_TimerGetExpiredTimes(&sNetComm.sSendTimer) >= U16_NETCOMM_WAIT_ACK_DELAY_COUNT)
    {
        // 停止定时器
        OS_TimerStop(&sNetComm.sSendTimer);
        // 设置“发送帧头”状态
        sNetComm.sSendControl.eState = eNETCOMM_STATE_HEADER;
        // 发送帧头字节
        sNetComm.pfSendByte(U8_NETCOMM_PKT_HEADER_BYTE);
    }
}

/*******************************************************************************
  * 函数名：NetComm_RecvByteHandler
  * 功  能：接收字节处理
  * 参  数：无
  * 返回值：无
  * 说  明：在接收中断中运行
*******************************************************************************/
void NetComm_RecvByteHandler(void)
{
    // 获取接收字节
    U8 u8RecvByte = sNetComm.pfRecvByte();

    // 检测到帧头
    if((u8RecvByte == U8_NETCOMM_PKT_HEADER_BYTE) &&
       (sNetComm.sRecvControl.u8LastByte == U8_NETCOMM_PKT_HEADER_BYTE))
    {
        // 设置“接收帧长”状态
        sNetComm.sRecvControl.eState = eNETCOMM_STATE_LENGTH;
    }
    else
    {
        switch(sNetComm.sRecvControl.eState)
        {
            // “接收帧长”状态
            case eNETCOMM_STATE_LENGTH:
            {
                // 帧长超过最大长度，丢弃该帧
                if(u8RecvByte > U8_NETCOMM_PKT_BODY_LEN)
                {
                    // 重置为空闲状态
                    sNetComm.sRecvControl.eState = eNETCOMM_STATE_IDLE;
                }
                // 帧长未超过最大长度
                else
                {
                    // 保存帧长
                    sNetComm.sRecvControl.sPacket.u8Length = u8RecvByte;
                    // 初始化接收指针和字节数
                    sNetComm.sRecvControl.pu8Byte =
                        sNetComm.sRecvControl.sPacket.au8ReserveBytes;
                    sNetComm.sRecvControl.u8ByteCount = 0;
                    // 设置“接收帧体”状态
                    sNetComm.sRecvControl.eState = eNETCOMM_STATE_BODY;
                }
            } break;
            // “接收帧体”状态
            case eNETCOMM_STATE_BODY:
            {
                // 上一个接收字节是帧头字节
                if((sNetComm.sRecvControl.u8ByteCount > 0) &&
                   (sNetComm.sRecvControl.u8LastByte ==
                    U8_NETCOMM_PKT_HEADER_BYTE))
                {
                    // 当前字节是冗余字节
                    if(u8RecvByte == U8_NETCOMM_PKT_REDUNDANT_BYTE)
                    {
                        // 更新接收字节数
                        sNetComm.sRecvControl.u8ByteCount++;
                    }
                    // 当前字节不是冗余字节
                    else
                    {
                        // 接收错误，重置为空闲状态
                        sNetComm.sRecvControl.eState = eNETCOMM_STATE_IDLE;
                    }
                }
                // 上一个接收字节不是帧头字节
                else
                {
                    // 保存当前字节
                    *(sNetComm.sRecvControl.pu8Byte++) = u8RecvByte;
                    // 当前字节不是帧头字节，更新接收字节数
                    if(u8RecvByte != U8_NETCOMM_PKT_HEADER_BYTE)
                    {
                        sNetComm.sRecvControl.u8ByteCount++;
                    }
                }

                // 接收完成
                if(sNetComm.sRecvControl.u8ByteCount >=
                   sNetComm.sRecvControl.sPacket.u8Length)
                {
                    // 重置为空闲状态
                    sNetComm.sRecvControl.eState = eNETCOMM_STATE_IDLE;
                    // 添加数据包到接收队列
                    OS_QueueAdd(&(sNetComm.sRecvQueue),
                              &(sNetComm.sRecvControl.sPacket));
                    // 添加任务，解析数据包
                    OS_TaskAdd(NetComm_ParseRecvPacket);
                }

            } break;
            // 其他状态
            default:
            {
                // 重置为空闲状态
                sNetComm.sRecvControl.eState = eNETCOMM_STATE_IDLE;
            } break;
        }
    }

    // 更新上一个接收字节
    sNetComm.sRecvControl.u8LastByte = u8RecvByte;
}

/*******************************************************************************
  * 函数名：NetComm_ParseRecvPacket
  * 功  能：解析接收数据包
  * 参  数：U32 arg
  * 返回值：无
  * 说  明：无
*******************************************************************************/
void NetComm_ParseRecvPacket(void)
{
    // 检查接收队列
    if(OS_QueueRemove(&(sNetComm.sRecvQueue), &(sNetComm.sRecvPacket)))
    {
        U8 u8Index;
        U16 u16Checksum = 0;
        U8* pu8Data = &sNetComm.sRecvPacket.u8Length;

        // 计算校验和
        for(u8Index=0; u8Index < sNetComm.sRecvPacket.u8Length; u8Index++)
        {
            u16Checksum += *pu8Data;
            // 不加密传输时，冗余字节计入校验和
            if(*pu8Data == U8_NETCOMM_PKT_HEADER_BYTE)
            {
                u16Checksum += U8_NETCOMM_PKT_REDUNDANT_BYTE;
            }
            pu8Data++;
        }
        // 校验和正确
        if((U8)u16Checksum == (*pu8Data))
        {
			// 收到应答帧处理
            if((sNetComm.sSendControl.bWaitAck) &&
               ((sNetComm.sSendControl.u8AckCmd == sNetComm.sRecvPacket.u8Cmd) || 
				(U8_NETCOMM_CMD_03_INVALID_CMD == sNetComm.sRecvPacket.u8Cmd)))
            {
                // 清除等待应答标志
                sNetComm.sSendControl.bWaitAck = FALSE;
                // 清零发送次数
                sNetComm.sSendControl.u8SentTimes = 0;
                // 超过发送延时时间，处理下个发送请求
                if(OS_TimerGetExpiredTimes(&sNetComm.sSendTimer) > 0)
                {
                    // 停止定时器
                    OS_TimerStop(&sNetComm.sSendTimer);
                    // 设置“发送空闲”状态
                    sNetComm.sSendControl.eState = eNETCOMM_STATE_IDLE;
                    // 检查发送请求
                    NetComm_CheckSendRequest();
                }
            }
            // 派遣消息
            NetComm_DispatchMsg(&(sNetComm.sRecvPacket));
        }
    }
}

/*******************************************************************************
  * 函数名：NetComm_CheckStatusTimerCallback
  * 功  能：检测状态定时器回调函数
  * 参  数：U32 arg
  * 返回值：无
  * 说  明：检测设备报警信息、设备状态是否发生变化，变化上报
*******************************************************************************/
static void NetComm_CheckStatusTimerCallback(void)
{
    // 更新设备状态数据
    NetComm_UpdataStatusData();

	// 更新报警数据
	NetComm_UpdataAlarmStatusData();
	
    // 报警状态改变
    if(NetComm_CheckAlarmStatus())
    {
        if(TRUE == NetComm_IsDevError())    
        {
            // 设备故障        汇报报警状态
            NetComm_SendAlarmStatusMsg();
	        // 重启定时器       定时报警
	        OS_TimerRestart(&sNetComm.sRptAlarmStatusTimer,
		                    U16_NETCOMM_RPT_ALARM_STATUS_MSEC,
		                    NetComm_SendAlarmStatusMsg);
        }
        else if(FALSE == NetComm_IsDevError())
        {
            // 故障消除 汇报报警状态
            NetComm_SendAlarmStatusMsg();
            // 停止报警定时器
            OS_TimerStop(&sNetComm.sRptAlarmStatusTimer);
        }
    }
	
    // 设备状态改变
    if(NetComm_CheckDevStatus())
    {
        // 汇报设备状态
        NetComm_SendDevStatusMsg();
    }
}

/*******************************************************************************
  * 函数名：NetComm_ChangeAlarm
  * 功  能：修改报警上报时间
  * 参  数：无
  * 返回值：无
  * 说  明：无
*******************************************************************************/
void NetComm_ChangeAlarm(void)
{
    // 将报警上报定时器修改为5S
    OS_TimerRestart(&sNetComm.sRptAlarmStatusTimer,
                    5000,
                    NetComm_SendAlarmStatusMsg);
}

/*******************************************************************************
  * 函数名：NetComm_StopAlarm
  * 功  能：停止报警
  * 参  数：无
  * 返回值：无
  * 说  明：无
*******************************************************************************/
void NetComm_StopAlarm(void)
{
    // 停止报警定时器
    OS_TimerStop(&sNetComm.sRptAlarmStatusTimer);
}

